/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package service

import (
	"did-service/bean/entity"
	"did-service/bean/param"
	"did-service/chain/artifact"
	"did-service/chain/client"
	"did-service/common/constant"
	"did-service/common/logger"
	"did-service/common/uuid"
	"did-service/config"
	"time"

	"github.com/go-xorm/xorm"
)

var log = logger.GetLogger("service")

type DidService struct {
	engine *xorm.Engine
}

func NewDidService(engine *xorm.Engine) *DidService {
	return &DidService{
		engine: engine,
	}
}

func (ds DidService) InsertDocument(walletId string, param *param.DocumentParam) error {
	createdTime, err := parseTime(param.Document.Created)
	if err != nil {
		log.Error("创建时间格式不正确")
		return err
	}
	updatedTime, err := parseTime(param.Document.Updated)
	if err != nil {
		log.Error("更新时间格式不正确")
		return err
	}
	uuidid := uuid.Generate()
	documentTable := &entity.DidDocument{
		Id:             uuidid,
		Did:            param.Did,
		WalletId:       walletId,
		ProofCreator:   param.Document.Proof.Creator,
		ProofType:      param.Document.Proof.Type,
		ProofSignature: param.Document.Proof.SignatureValue,
		Status:         "1",
		Note:           "",
		Version:        param.Document.Version,
		AuthPubKey:     param.Document.Authentication[0],
		RecoveryPubKey: param.Document.Recovery[0],
		CreatedTime:    createdTime,
		UpdatedTime:    updatedTime,
	}
	_, err = ds.engine.InsertOne(documentTable)
	if err != nil {
		log.Error(err)
		return err
	}
	return nil
}

func (ds DidService) DeleteDocument(param *param.DocumentParam) error {
	document := entity.DidDocument{
		Did:    param.Did,
		Status: "0",
	}
	_, err := ds.engine.Where("did = ?", document.Did).Cols("status").Update(document)
	if err != nil {
		log.Error(err)
		return err
	}

	return nil
}

func (srv *DidService) getChainClient() *client.IchainClient {
	userEntities := make([]*entity.DidChainUser, 0)
	err := srv.engine.Find(&userEntities)
	if err != nil {
		log.Errorf("get ichain client, query chain user error: %v", err)
		return nil
	}
	if len(userEntities) <= 0 {
		log.Errorf("get ichain client, can not find user")
		return nil
	}

	consenserEntities := make([]*entity.DidChainConsenser, 0)
	err = srv.engine.Find(&consenserEntities)
	if err != nil {
		log.Errorf("get ichain client, query consenser error: %v", err)
		return nil
	}
	if len(consenserEntities) <= 0 {
		log.Errorf("get ichain client, can not find consenser")
		return nil
	}
	consensers := make([]artifact.Node, 0)
	for _, ce := range consenserEntities {
		consenser := artifact.Node{
			NodeName: ce.Name,
			Host:     ce.Host,
			Port:     ce.Port,
		}
		consensers = append(consensers, consenser)
	}

	orgEntities := make([]*entity.DidChainOrg, 0)
	err = srv.engine.Find(&orgEntities)
	if err != nil {
		log.Errorf("get ichain client, query org error: %v", err)
		return nil
	}
	if len(orgEntities) <= 0 {
		log.Errorf("get ichain client, can not find org")
		return nil
	}
	orgs := make([]artifact.Org, 0)
	nodes := make([]string, 0)
	for _, orgEntity := range orgEntities {
		nodeEntities := make([]*entity.DidChainNode, 0)
		err := srv.engine.Where("org_id = ?", orgEntity.Id).Find(&nodeEntities)
		if err != nil {
			log.Errorf("get ichain client, query node error: %v", err)
			return nil
		}
		if len(nodeEntities) <= 0 {
			log.Errorf("get ichain client, can not find node, org id = %s", orgEntity.Id)
			return nil
		}

		org := artifact.Org{
			OrgName:    orgEntity.OrgName,
			IamId:      orgEntity.IamId,
			CryptoPath: config.Get("did.chain.org-crypto-path", orgEntity.CryptoPath),
			Nodes:      []artifact.Node{},
		}
		for _, nodeEntity := range nodeEntities {
			node := artifact.Node{
				NodeName: nodeEntity.Name,
				Host:     nodeEntity.Host,
				Port:     nodeEntity.Port,
			}
			org.Nodes = append(org.Nodes, node)
			nodes = append(nodes, node.NodeName)
		}
		orgs = append(orgs, org)
	}
	alg := config.Get("did.chain.alg", constant.AlgGM)
	hash := constant.HashAlgSm3

	if alg == constant.AlgSW {
		hash = constant.HashAlgSha256
	} else {
		alg = constant.AlgGM
		hash = constant.HashAlgSm3
	}

	cc := client.NewClient(client.Config{
		OrgName:     config.Get("did.chain.org-name", ""),
		ServiceName: config.Get("did.chain.service-name", ""),
		ChainName:   config.Get("did.chain.chain-name", ""),
		ChainLog:    config.Get("did.chain.log", constant.DefaultChainLogLevel),
		Alg:         alg,
		HashAlg:     hash,
		Orgs:        orgs,
		Consensers:  consensers,
		Nodes:       nodes,
		Cert:        []byte(userEntities[0].Cert),
		PrvKey:      []byte(userEntities[0].PrvKey),
	})
	err = cc.Initialize()
	if err != nil {
		return nil
	}
	return cc
}

func parseTime(timeStr string) (time.Time, error) {
	layout := constant.TimeLayout
	t, err := time.ParseInLocation(layout, timeStr, time.Local)
	if err != nil {
		return time.Time{}, err
	}
	return t, nil
}
